///FEN 2005.02.16
///PQC 2005.04.27 Namespace changed to Noea.Graph
///FEN 2008.10.28 Using generics
/// <summary>
/// Implements IBasicGraph using adjacency lists.
/// </summary>

using System.Collections;
using System.Collections.Generic;
namespace InSafetyAirServer.Model
{
    public class Graph<T, E> : IGraph<T, E>
    {
        public IList<Vertex<T, E>> _vertices;

        public Graph()
        {
            _vertices = new List<Vertex<T, E>>();
        }


        #region IBasicGraph Members

        public bool IsEmpty()
        {
            return _vertices.Count == 0;
        }

        public void AddVertex(Vertex<T, E> v)
        {
            _vertices.Add(v);
        }

        public void AddEdge(Vertex<T, E> from, Vertex<T, E> to, E data)
        {
            from.AddEdge(to, data);
        }

        public void RemoveEdge(Vertex<T, E> from, Vertex<T, E> to, E data)
        {

            from.RemoveEdge(getEdgeExtended(from, to, data));
        }

        public bool ContainsVertex(Vertex<T, E> vertex)
        {
            return _vertices.Contains(vertex);
        }

        public bool IsAdjacent(Vertex<T, E> startVertex, Vertex<T, E> endVertex)
        {
            IEnumerator<Edge<T, E>> enumerator = startVertex.Edges().GetEnumerator();
            while (enumerator.MoveNext())
            {
                if(enumerator.Current.To == endVertex) return true;
            }
            return false;
        }

        public Vertex<T, E> getVertex(T obj)
        {
            IEnumerator<Vertex<T, E>> enumerator = _vertices.GetEnumerator();
            while (enumerator.MoveNext())
            {
                if (enumerator.Current.Object.Equals(obj)) return enumerator.Current;
            }
            return null;
        }

        public Edge<T, E> getEdge(Vertex<T, E> startVertex, Vertex<T, E> endVertex)
        {
            IEnumerator<Edge<T, E>> enumerator = startVertex.Edges().GetEnumerator();
            while (enumerator.MoveNext())
            {
                if (enumerator.Current.To == endVertex) return enumerator.Current;
            }
            return null;
        }

        public Edge<T, E> getMinEdge(Vertex<T, E> startVertex, Vertex<T, E> endVertex, string field)
        {
            Edge<T, E> minEdge = null;
            int min = int.MaxValue;
            foreach(Edge<T, E> e in startVertex.Edges())
            {
                if (e.From.Equals(startVertex) && e.To.Equals(endVertex))
                {
                    int val = (int)e.Data.GetType().GetProperty(field).GetValue(e.Data, null);
                    if (val < min)
                    {
                        min = val;
                        minEdge = e;
                    }
                }
            }
            return minEdge;
        }

        public Edge<T, E> getEdgeExtended(Vertex<T, E> startVertex, Vertex<T, E> endVertex, E data)
        {
            IEnumerator<Edge<T, E>> enumerator = startVertex.Edges().GetEnumerator();
            while (enumerator.MoveNext())
            {
                if (enumerator.Current.To == endVertex && enumerator.Current.Data.Equals(data)) return enumerator.Current;
            }
            return null;
        }

        public IList<Vertex<T, E>> GetAdjacencies(Vertex<T, E> vertex)
        {
            IList<Vertex<T, E>> adjacencies = new List<Vertex<T, E>>();
            foreach(Edge<T, E> edge in vertex.Edges())
            {
                adjacencies.Add(edge.To);
            }
            return adjacencies;
        }

        public IEnumerator<Vertex<T, E>> Vertices()
        {
            return _vertices.GetEnumerator();
        }

        public int GetNoOfVertices()
        {
            return _vertices.Count;
        }

        public int GetNoOfEdges()
        {
            int count = 0;
            foreach(Vertex<T, E> vertex in _vertices)
            {
                count += vertex.Edges().Count;
            }
            return count;
        }

        public void Clear()
        {
            foreach (Vertex<T, E> vertex in _vertices)
            {
                vertex.Edges().Clear();
            }
            _vertices.Clear();
        }

        public void BFS(Vertex<T, E> v)
        {
            Queue<Vertex<T, E>> Q = new Queue<Vertex<T, E>>();
            // VISIT THE NODE
            v.Mark = true;
            Q.Enqueue(v);
            while(Q.Count > 0)
            {
                Vertex<T, E> w = Q.Dequeue();
                foreach (Vertex<T, E> u in GetAdjacencies(w))
                {
                    if(u.Mark == false)
                    {
                        //VISIT THE NODE
                        u.Mark = true;
                        Q.Enqueue(u);
                    }
                }
            }
            Unmark();
        }

        public void DFS(Vertex<T, E> v)
        {
            // VISIT NODE
            v.Mark = true;
            foreach (Vertex<T, E> u in GetAdjacencies(v))
            {
                if (u.Mark == false)
                {
                    DFS(u);
                }
            }
        }

        public IList<Edge<T, E>> Edges()
        {
            IList<Edge<T, E>> edges = new List<Edge<T, E>>();
            foreach (Vertex<T, E> vertex in _vertices)
            {
                foreach(Edge<T, E> edge in vertex.Edges())
                {
                    edges.Add(edge);
                }
            }
            return edges;
        }

        public int IndexOf(Vertex<T, E> v)
        {
            return _vertices.IndexOf(v);
        }

        public void Unmark()
        {
            foreach (Vertex<T, E> v in _vertices)
            {
                v.Mark = false;
            }
        }

        #endregion
    }
}
